#pragma once

#include "Namespace.h"

#include "Constraint.h"
#include "ElementImport.h"
#include "NamedElement.h"
#include "PackageImport.h"
#include "PackageableElement.h"

namespace uml {

void Namespace::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    PartialElement::fromJson(json, errorList);
    JsonSerializable::fromJsonArray(elementImports_, "elementImports", json, errorList);
    JsonSerializable::fromJsonArray(importedMembers_, "importedMembers", json, errorList);
    JsonSerializable::fromJsonArray(members_, "members", json, errorList);
    JsonSerializable::fromJsonArray(ownedMembers_, "ownedMembers", json, errorList);
    JsonSerializable::fromJsonArray(ownedRules_, "ownedRules", json, errorList);
    JsonSerializable::fromJsonArray(packageImports_, "packageImports", json, errorList);
}

void Namespace::toJson(QJsonObject& json) {
    PartialElement::toJson(json);
    JsonSerializable::toJsonArray(elementImports_, "elementImports", json);
    JsonSerializable::toJsonArray(importedMembers_, "importedMembers", json);
    JsonSerializable::toJsonArray(members_, "members", json);
    JsonSerializable::toJsonArray(ownedMembers_, "ownedMembers", json);
    JsonSerializable::toJsonArray(ownedRules_, "ownedRules", json);
    JsonSerializable::toJsonArray(packageImports_, "packageImports", json);
}

/// <summary>
/// excludeCollisions
    /// The query excludeCollisions() excludes from a set of PackageableElements any
    /// that would not be distinguishable from each other in this Namespace.
/// </summary>
    /// <code>
    /// result = (imps->reject(imp1  | imps->exists(imp2 | not imp1.isDistinguishableFrom(imp2, self))))
    /// </code>
shared_ptr<vector<PackageableElement>> Namespace::excludeCollisions(vector<shared_ptr<PackageableElement>> imps) const
{
    return nullptr;
}

/// <summary>
/// getNamesOfMembers
    /// The query getNamesOfMember() gives a set of all of the names that a member would
    /// have in a Namespace, taking importing into account. In general a member can have
    /// multiple names in a Namespace if it is imported more than once with different
    /// aliases.
/// </summary>
    /// <code>
    /// result = (if self.ownedMember ->includes(element)
    /// then Set{element.name}
    /// else let elementImports : Set(ElementImport) = self.elementImport->select(ei | ei.importedElement = element) in
    ///   if elementImports->notEmpty()
    ///   then
    ///      elementImports->collect(el | el.getName())->asSet()
    ///   else 
    ///      self.packageImport->select(pi | pi.importedPackage.visibleMembers().oclAsType(NamedElement)->includes(element))-> collect(pi | pi.importedPackage.getNamesOfMember(element))->asSet()
    ///   endif
    /// endif)
    /// </code>
shared_ptr<vector<QString>> Namespace::getNamesOfMembers(shared_ptr<NamedElement> element) const
{
    return nullptr;
}

/// <summary>
/// importMembers
    /// The query importMembers() defines which of a set of PackageableElements are
    /// actually imported into the Namespace. This excludes hidden ones, i.e., those
    /// which have names that conflict with names of ownedMembers, and it also excludes
    /// PackageableElements that would have the indistinguishable names when imported.
/// </summary>
    /// <code>
    /// result = (self.excludeCollisions(imps)->select(imp | self.ownedMember->forAll(mem | imp.isDistinguishableFrom(mem, self))))
    /// </code>
shared_ptr<vector<PackageableElement>> Namespace::importMembers(vector<shared_ptr<PackageableElement>> imps) const
{
    return nullptr;
}

/// <summary>
/// membersAreDistinguishable
    /// The Boolean query membersAreDistinguishable() determines whether all of the
    /// Namespace's members are distinguishable within it.
/// </summary>
    /// <code>
    /// result = (member->forAll( memb |
    ///    member->excluding(memb)->forAll(other |
    ///        memb.isDistinguishableFrom(other, self))))
    /// </code>
bool Namespace::membersAreDistinguishable() const
{
    return false;
}

/// <summary>
/// elementImports getter
/// References the ElementImports owned by the Namespace.
/// </summary>
vector<shared_ptr<ElementImport>>& Namespace::elementImports() {
    return elementImports_;
}

/// <summary>
/// importedMembers getter
/// References the PackageableElements that are members of this Namespace as a
/// result of either PackageImports or ElementImports.
/// </summary>
vector<shared_ptr<PackageableElement>>& Namespace::importedMembers() {
    return importedMembers_;
}

/// <summary>
/// members getter
/// A collection of NamedElements identifiable within the Namespace, either by being
/// owned or by being introduced by importing or inheritance.
/// </summary>
vector<shared_ptr<NamedElement>>& Namespace::members() {
    return members_;
}

/// <summary>
/// ownedMembers getter
/// A collection of NamedElements owned by the Namespace.
/// </summary>
vector<shared_ptr<NamedElement>>& Namespace::ownedMembers() {
    return ownedMembers_;
}

/// <summary>
/// ownedRules getter
/// Specifies a set of Constraints owned by this Namespace.
/// </summary>
vector<shared_ptr<Constraint>>& Namespace::ownedRules() {
    return ownedRules_;
}

/// <summary>
/// packageImports getter
/// References the PackageImports owned by the Namespace.
/// </summary>
vector<shared_ptr<PackageImport>>& Namespace::packageImports() {
    return packageImports_;
}

} // namespace uml
